Sample Scripting Addition
Listing 3-3 demonstrates the basic structure of a scripting addition handler and its associated'aete'
resource. It is called "Play Sound Scripting Addition" and is written in MPW C.
Listing 3-3 Play Sound scripting addition
//////////////////////////////////////////////////////////////////// // // PlaySnd.c // // The Play Sound Scripting Addition // Copyright ®1993 Apple Computer Inc. // All rights reserved. // // Written by: Donald Olson // // To build: // C -b "PlaySnd.c" -d SystemSevenOrLater // Rez -a -o "Play Sound" -t osax -c ascr 'PlaySnd.r' // Link -p -w -t osax -c ascr -rt osax=1000 -m PLAYSNDENTRY -sg// "AEVTaevtplsn" -ra "AEVTaevtplsn"=resSysHeap,resLocked // "PlaySnd.c.o"
// "{CLibraries}"StdCLib.o
// "{Libraries}"Runtime.o
// "{Libraries}"Interface.o
// -o "Play Sound" // //////////////////////////////////////////////////////////////////// #include <Resources.h>#include <Sound.h>#include <AppleEvents.h> #define kAsync true // asynchronous play #define kQuietNow true // quiet channel now #define kSndType 'snd ' // resource type we're //looking for #define typeIntlText 'itxt' // defined in AERegistry.r #define typeStyledText 'STXT' // defined in AERegistry.r //////////////////////////////////////////////////////////////////// // // PlaySndEntry () // // The direct parameter is either a name or an ID of the 'snd ' // resource to play. // ////////////////////////////////////////////////////////////////// pascal OSErr PlaySndEntry( AppleEvent *theAEEvent, AppleEvent *theReply, long theRefCon) { /* Function Prototypes */ OSErr PlaySound(Handle theSoundHdl); /* variables */ OSErr theErr = noErr; DescType typeCode; Size sizeOfParam, actualSize; Handle theSndHandle = nil; /*just clear our */ /* sound handle*/ SndChannelPtr theSndChan = NULL; /*NULL pointer to */ /* a sound channel*/ short ourRezID = 0; Str255 ourRezName; FSSpec ourSoundFile; short ourFileRef, curResFile; /* Get the data type from direct object by using AESizeOfParam. We use this call instead of AEGetParamDesc or AEGetParamPtr because we are looking for one of several types. In this way we can determine the type and move its data directly into a variable instead of an AEDesc. Now we don't have to worry about disposing of the AEDesc later. */ theErr = AESizeOfParam( theAEEvent, keyDirectObject, &typeCode, &sizeOfParam); if(theErr != noErr){ /* If we fail here, just return the error. We don't need to do any cleanup, because we've allocated nothing on the heap yet. The Apple Event Manager automatically adds the error number to the reply as keyErrorNumber for nonzero handler returns. */ return theErr; } else{ if((typeCode == typeChar) || (typeCode == typeStyledText) || (typeCode == typeIntlText)) { /* If one of these types match, we've been passed a name of a resource. Use AEGetParamPtr to move it into our string and transform it into a Pascal type string that we can pass to GetNamedResource. If we get an error in AEGetParamPtr, just let it fall through to the bottom of this handler. */ theErr = AEGetParamPtr(theAEEvent, keyDirectObject, typeChar, &typeCode, (Ptr)&ourRezName, sizeof(ourRezName), &actualSize); if(theErr == noErr) { /* 'C' string has a null as last char */ ourRezName[actualSize] = '\0'; /* convert to Pascal string */ c2pstr((char*) ourRezName); /* now grab the 'snd ' resource by name*/ theSndHandle = GetNamedResource(kSndType, (ConstStr255Param)ourRezName); /* check the error */ theErr = ResError(); if(theErr == noErr) theErr = PlaySound(theSndHandle); /*call our */ /* sound code*/ } } else { if(typeCode == typeLongInteger) { /* If we get a typeLongInteger, the user wants us to play a sound by its resource ID. AppleScript will send us a long here and the Resource Manager wants us to pass in a short, so let's have the Apple Event Manager coerce it to a short for us. */ theErr = AEGetParamPtr(theAEEvent, keyDirectObject, typeShortInteger, &typeCode, (Ptr)&ourRezID, sizeof(ourRezID), &actualSize); if(theErr == noErr) { /* now grab the 'snd ' resource by ID */ theSndHandle = GetResource (kSndType, ourRezID); /* check the error */ theErr = ResError(); if(theErr == noErr) theErr = PlaySound(theSndHandle); /*call our */ /* sound code*/ } } else { if(typeCode == typeAlias) { /* If we receive a typeAlias, the user is asking us to play a sound file. We want to use a FSSpec to open the resource file, so once again we ask the Apple Event Manager to coerce data to the type we need. */ theErr = AEGetParamPtr(theAEEvent, keyDirectObject, typeFSS, &typeCode, (Ptr)&ourSoundFile, sizeof(ourSoundFile), &actualSize); if(theErr != noErr) return theErr; /* save off our current resource file */ curResFile = CurResFile(); /* open our resource file for reading */ ourFileRef = FSpOpenResFile(&ourSoundFile, fsRdPerm); /* check the error */ theErr = ResError(); if(theErr != noErr) return theErr; /* make our files resource fork top in the chain */ UseResFile(ourFileRef); /* Since we don't know for sure the resource id of the targeted files 'snd ' resource, let's just get the first (and supposedly only) one. */ theSndHandle = Get1IndResource(kSndType, 1); /* check the error */ theErr = ResError(); if(theErr == noErr) theErr = PlaySound(theSndHandle); /* restore resource chain and close our file */ UseResFile(curResFile); CloseResFile(ourFileRef); } else /* wasn't a string, alias, or number so exit */ return errAEEventNotHandled; } } } /* dispose 'snd ' handle if necessary */ if(theSndHandle != nil) ReleaseResource(theSndHandle); return theErr; } //////////////////////////////////////////////////////////////////// // // PlaySound(Handle theSoundHdl) // // This is the code to play a 'snd '. // ////////////////////////////////////////////////////////////////// OSErr PlaySound(Handle theSoundHdl) { /* our variables */ OSErr theErr = noErr; SndChannelPtr theSndChan = NULL; /*NULL pointer to a */ /* sound channel*/ /* open a channel so we can do synchronous play */ theErr = SndNewChannel (&theSndChan, sampledSynth, initMono, NULL); if (theErr == noErr) /* play that sound */ theErr = SndPlay (theSndChan, theSoundHdl, !kAsync); /* dispose of the channel, if the sound channel was allocated */ if (theSndChan != NULL) SndDisposeChannel(theSndChan, !kQuietNow); return theErr; } /********************************************** Resource file for PlaySnd.c Copyright ®1993 Apple Computer Inc. All rights reserved. Written by Donald Olson ***********************************************/ #include "Types.r"#include "SysTypes.r"#include "AEUserTermTypes.r" /* our version 1 and 2 resources */ resource 'vers' (1) { 0x1, 0x0, final, 0x0, verUS, "1.0", "1.0, Copyright ® 1993 Apple Comput" "er, Inc. All rights reserved."}; resource 'vers' (2) { 0x1, 0x0, final, 0x0, verUS, "1.0", "(by Donald Olson)"}; /* This string is used when the user double-clicks on a scripting addition file. Since it contains nothing that can be opened or printed, the user gets this in a dialog box (thanks to the system for making this happen). */ resource 'STR ' (-16397) { "This document can not be opened or printed." " It extends the functionality of AppleScript(TM) " "and should be placed in the Scripting Additions" "folder found in the Extensions folder of your" " System Folder."}; /* Our 'aete' resource. It's here that we describe to AppleScript the syntax of our scripting addition. Notice that the comment field contains information about the event and its parameters. These comments can be displayed by the Script Editor if the user selects this scripting addition in the terminology browser invoked when the user chooses the Open Dictionary menu. */ resource 'aete' (0, "Play Sound scripting addition") { 0x0, -0x70, english, roman, { /* array Suites: 1 elements */ /* [1] */ "System Object Suite", "", 'syso', 1, 1, { /* array Events: 1 elements */ /* [1] */ "play sound", " This is the syntax for invoking this scripting" " addition from AppleScript(TM).", 'aevt', 'plsn', noReply, "The reply is not required", replyOptional, singleItem, notEnumerated, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, '****', "id or name of 'snd ' resource to play" " or path to a sound file", directParamRequired, singleItem, notEnumerated, doesntChangeState, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, reserved, { /* array OtherParams: 0 elements */ } }, { /* array Classes: 0 elements */ }, { /* array ComparisonOps: 0 elements */ }, { /* array Enumerations: 0 elements */ } } };